<?php

if (!defined("PATH_HD"))
    exit('No direct script access allowed');

// .-----------------------------------------------------------------------------------
// |  Software: [HDPHP framework]
// |   Version: 2013.01
// |      Site: http://www.hdphp.com
// |-----------------------------------------------------------------------------------
// |    Author: 向军 <houdunwangxj@gmail.com>
// | Copyright (c) 2012-2013, http://houdunwang.com. All Rights Reserved.
// |-----------------------------------------------------------------------------------
// |   License: http://www.apache.org/licenses/LICENSE-2.0
// '-----------------------------------------------------------------------------------

/**
 * URL处理类
 * @package     core
 * @author      后盾向军 <houdunwangxj@gmail.com>
 */
final class Url {

    static private $isSetApp; //入口文件是否定义应用
    static private $queryString; //原始URL参数
    static private $isPathinfo; //是否为pathinfo

    /**
     * 解析URL为GET数组
     * @access public
     */

    static public function parseUrl() {
        self::getQueryString(); //设置URL的QUERY_STRING
        self::$isSetApp = defined("APP") ? true : false; //是否定义APP
        $get = array(); //get参数
        if (self::$isPathinfo) {
            $info = explode('/', self::$queryString);
            if (!defined("APP")) {
                if ($info [0] != C("VAR_APP")) {//应用
                    $get [C('VAR_APP')] = $info [0];
                    array_shift($info);
                } else {
                    $get [C('VAR_APP')] = $info [1];
                    array_shift($info);
                    array_shift($info);
                }
            }
            if (isset($info[0]) && $info[0] != C('VAR_CONTROL')) {//控制器
                $get [C('VAR_CONTROL')] = isset($info [0]) ? $info[0] : C("DEFAULT_CONTROL");
                array_shift($info);
            } else {
                $get [C('VAR_CONTROL')] = isset($info [1]) ? $info[1] : C("DEFAULT_CONTROL");
                array_shift($info);
                array_shift($info);
            }
            if (isset($info[0]) && $info[0] != C("VAR_METHOD")) {//方法
                $get [C('VAR_METHOD')] = isset($info [0]) ? $info[0] : C("DEFAULT_METHOD");
                array_shift($info);
            } else {
                $get [C('VAR_METHOD')] = isset($info [1]) ? $info[1] : C("DEFAULT_METHOD");
                array_shift($info);
                array_shift($info);
            }
            $count = count($info);
            for ($i = 0; $i < $count;) {
                $get [$info [$i]] = isset($info [$i + 1]) ? $info [$i + 1] : '';
                $i+=2;
            }
        }
        $_GET = array_merge($_GET, $get);
        //去除空参数
        $gets = array();
        foreach ($_GET as $k => $v) {
            if ($v == '') {
                continue;
            }
            $gets[$k] = $v;
        }
        $_GET = $gets;
        self::setUrlConst();
        $_control = C('VAR_CONTROL');
        $_method = C('VAR_METHOD');
        unset($_GET[$_control]);
        unset($_GET[$_method]);
        //定义应用组删除应用GET
        if (defined("APP_GROUP")) {
            $_app = C('VAR_APP');
            unset($_GET[$_app]);
        }
        //兼容模式删除其$_GET变量
        if (C('URL_TYPE') == 2) {
            $_pathinfo_var = C('PATHINFO_VAR');
            unset($_GET[$_pathinfo_var]);
        }
    }

    /**
     * 设置URL常量
     * @access private
     * @return void
     */
    static private function setUrlConst() {
        //当前应用
        if (!defined("APP")) {
            $app = isset($_GET [C('VAR_APP')]) && !empty($_GET[C('VAR_APP')]) ? $_GET [C('VAR_APP')] : C("DEFAULT_APP");
            define("APP", $app);
        }
        //当前控制器
        $_control = (isset($_GET [C('VAR_CONTROL')]) && !empty($_GET[C('VAR_CONTROL')]) ? $_GET [C('VAR_CONTROL')] : C("DEFAULT_CONTROL"));
        $control = ucwords(preg_replace("/_([A-Z])/ie", "strtoupper(htmlspecialchars (strip_tags('\\1')));", $_control));
        define("CONTROL", $control);
        //当前动作方法
        define("METHOD", (isset($_GET [C('VAR_METHOD')]) && !empty($_GET[C('VAR_METHOD')]) ? $_GET [C('VAR_METHOD')] : C("DEFAULT_METHOD")));

        if (defined("APP_GROUP")) {
            define("__APP_GROUP__", __ROOT__ . '/' . APP_GROUP);
        }
        $url_type = C("URL_TYPE"); // URL类型    1:pathinfo  2:普通模式  3:rewrite 重写
        switch ($url_type) {
            case 1:
                define("__APP__", __WEB__ . (self::$isSetApp ? '' : '/' . APP));
                define("__CONTROL__", __APP__ . '/' . CONTROL);
                define("__METH__", __CONTROL__ . '/' . METHOD);
                break;
            case 2:
                define("__APP__", __WEB__ . (self::$isSetApp ? '' : '?' . C("VAR_APP") . '=' . APP));
                define("__CONTROL__", __APP__ . (self::$isSetApp ? '?' : '&') . C('VAR_CONTROL') . '=' . CONTROL);
                define("__METH__", __CONTROL__ . '&' . C('VAR_METHOD') . '=' . METHOD);
                break;
            default:
                $pathinfo_var = C("PATHINFO_VAR");
                define("__APP__", __WEB__ . '?' . $pathinfo_var . '=' . (self::$isSetApp ? '' : '/' . APP));
                define("__CONTROL__", __APP__ . '/' . CONTROL);
                define("__METH__", __CONTROL__ . '/' . METHOD);
        }
        define('__URL__', self::getFormatUrl()); //解析生成全路径URL
    }

    /**
     * 根据配置项获得格式化后的URL地址
     * @access private
     * @return type
     */
    static private function getFormatUrl() {
        $pathinfo_dli = C("PATHINFO_Dli");
        $url_type = C("URL_TYPE"); // URL类型    1:pathinfo  2:普通模式  3:rewrite 重写
        $url = ''; //组合后的URL地址
        switch ($url_type) {
            case 1:
                foreach ($_GET as $k => $v) {
                    if (in_array($k, array(C("VAR_APP"), C("VAR_CONTROL"), C("VAR_METHOD"))))
                        continue;
                    $url.=$pathinfo_dli . $k . $pathinfo_dli . $v;
                }
                $url = trim($url, $pathinfo_dli);
                $url = __METH__ . $pathinfo_dli . $url;
                break;
            case 2:
                foreach ($_GET as $k => $v) {
                    $url.=$k . '=' . $v . '&';
                }
                $url = trim($url, '&');
                $url = __WEB__ . '?' . $url;
                break;
        }
        return rtrim($url, $pathinfo_dli);
    }

    /**
     * 设置URL的QUERY_STRING
     * @access private
     * @return void
     */
    static private function getQueryString() {
        $pathinfo_var = C("PATHINFO_VAR");
        if (!isset($_GET[$pathinfo_var]) && (!isset($_SERVER['PATH_INFO']) || empty($_SERVER['PATH_INFO']))) {

            $query_string = $_SERVER['QUERY_STRING'];
            $routeUrl = self::parseRoute($query_string); //路由操作
            //不匹配路由
            if ($query_string == $routeUrl) {
                self::$isPathinfo = FALSE;
            } else {
                //匹配路由
                $_GET = array();
                $getS = array();
                parse_str($routeUrl, $getS);
                $_GET = $getS;
            }
            return;
        }
        self::$isPathinfo = true;
        self::$queryString = isset($_GET[$pathinfo_var]) ? $_GET[$pathinfo_var] : str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['REQUEST_URI']);

        self::$queryString = trim(self::$queryString, '/');
        $pathinfo_dli = C("PATHINFO_Dli");
        self::delPathinfoHtml();
        if (C("route")) {
            self::$queryString = self::parseRoute(self::$queryString); //解析处理URL路由规则
        }
        $url = explode("?", self::$queryString);
        if (count($url) >= 2) {
            //得出控制器与方法
            $arr = explode($pathinfo_dli, $url[0]);
            if (defined("APP")) {
                self::$queryString = count($arr) == 1 ? $url[0] . $pathinfo_dli . C("DEFAULT_METHOD") : self::$queryString;
            } else {
                self::$queryString = count($arr) == 2 ? $url[0] . $pathinfo_dli . C("DEFAULT_METHOD") . $pathinfo_dli . $arr[1] : self::$queryString;
            }
        }
        self::$queryString = trim(preg_replace("/\?|=|&/i", '/', self::$queryString), '/');
        self::$queryString = str_replace($pathinfo_dli, '/', self::$queryString);
    }

    /**
     * 清除伪静态变量
     * @access private
     * @return void
     */
    static private function delPathinfoHtml() {
        $pathinfo_html = "." . trim(C("PATHINFO_HTML"), "."); //清除伪静态变量
        self::$queryString = str_ireplace($pathinfo_html, "", self::$queryString);
        self::$queryString = rtrim(self::$queryString, '?');
    }

    /**
     * 解析路径规则
     * @access private
     * @param string $query_string  URL字符串
     * @return string
     */
    static private function parseRoute($query_string) {
        $route = C("route");
        if (!$route)
            return $query_string;
        $search = array(
            "/(:year)/i",
            "/(:month)/i",
            "/(:day)/i",
            "/(:num)/i",
            "/(:any)/i",
            "/(:\w+)/i",
            "/\//",
        );
        $replace = array(
            "\d{2,4}",
            "\d{1,2}",
            "\d{1,2}",
            "\d+",
            ".+",
            "(\w+)",
            "\/",
        );

        foreach ($route as $k => $v) {
            $v = rtrim($v, '/');
            //正则路由
            if (preg_match("/^\/.*\/[isUx]*$/i", $k)) {
                if (preg_match($k, $query_string)) {
                    $v = str_replace("#", '\\', $v);
                    $query_string = preg_replace($k, $v, $query_string);
                    break;
                }
                continue;
            }
            //非正则路由
            $preg_k = "/^\/?" . preg_replace($search, $replace, $k) . "$/i"; //将:year等替换
            //不满足路由规则
            if (!preg_match($preg_k, $query_string)) {
                continue;
            }
            //路径规则不存在:year等
            $routeVar = ''; //获得:page等
            preg_match("/[^:\sa-z0-9]/i", $k, $routeVar);
            //路由规则没有设置GET参数
            if (!$routeVar) {//路由中没有:year等设置
                $query_string = $v;
                break;
            }
            $role = explode($routeVar[0], $k);
            $urls = explode($routeVar[0], $query_string);
            $query_string = $v; //匹配成功的路由规则
            $getName = ''; //GET参数名称
            foreach ($role as $m => $n) {
                if (!strstr($n, ":")) {
                    continue;
                }
                $getName = str_replace(":", "", $n);
                $_GET[$getName] = $urls[$m];
            }
            break;
        }
        return $query_string;
    }

    /**
     * 将URL按路由规则进行处理
     * U()函数等使用
     * @access public
     * @param  string $url url字符串不含__WEB__.'?|/'
     * @return string
     */
    static public function toRoute($url) {
        $route = C("route");
        //未定义路由规则
        if (!$route) {
            return $url;
        }
        foreach ($route as $routeKey => $routeVal) {
            $routeKey = trim($routeKey);
            //正则路由
            if (substr($routeKey, 0, 1) === '/') {
                $regGroup = array(); //识别正则路由中的原子组
                preg_match_all("/\(.*?\)/", $routeKey, $regGroup, PREG_PATTERN_ORDER);
                $searchRegExp = $routeVal; //匹配URL的正则
                //将正则路由的$v中的值#1换成$r中的(\d+)形式
                for ($i = 0, $total = count($regGroup[0]); $i < $total; $i++) {
                    $searchRegExp = str_replace('#' . ($i + 1), $regGroup[0][$i], $searchRegExp);
                }
                $urlArgs = array(); //URL参数
                preg_match_all("@" . $searchRegExp . "@", $url, $urlArgs, PREG_SET_ORDER);
                //满足路由规则
                if ($urlArgs) {
                    //清除路由中的/$与/正则边界
                    $routeUrl = trim(str_replace(array('/^', '$/'), '', $routeKey), '/');
                    foreach ($regGroup[0] as $k => $v) {
                        $routeUrl = str_replace($v, $urlArgs[0][$k + 1], $routeUrl);
                    }
                    return trim($routeUrl, '/');
                }
            } else {
                //获得如 "info/:city_:row" 中的:city与:row
                $routeGetVars = array();
                //普通路由处理
                preg_match_all('/:([a-z]*)/', $routeKey, $routeGetVars, PREG_PATTERN_ORDER); //获得路由规则中以:开始的变量
                $getRouteUrl = $routeVal;
                switch (C("URL_TYPE")) {
                    case 1:
                        $getRouteUrl.= '/';
                        foreach ($routeGetVars[1] as $getK => $getV) {
                            $getRouteUrl.= $getV . '/(.*)' . '/';
                        }
                        $getRouteUrl = '@' . trim($getRouteUrl, '/') . '@';
                        break;
                    case 2:
                        $getRouteUrl.= '&';
                        foreach ($routeGetVars[1] as $getK => $getV) {
                            $getRouteUrl.= $getV . '=(.*)' . '&';
                        }
                        $getRouteUrl = '@' . trim($getRouteUrl, '&') . '@';
                        break;
                }
                $getArgs = array();
                preg_match_all($getRouteUrl, $url, $getArgs, PREG_SET_ORDER);
                if ($getArgs) {
                    //去除路由中的传参数如:uid
                    $newUrl = $routeKey;
                    foreach ($routeGetVars[0] as $rk => $getName) {
                        $newUrl = str_replace($getName, $getArgs[0][$rk + 1], $newUrl);
                    }
                    return $newUrl;
                }
            }
        }
        return $url;
    }

    /**
     * 移除URL中的指定GET变量
     * 提供函数url_remove_param()等使用，只保留$_GET中的$var值
     * @access public
     * @param string $var  要移除的GET变量
     * @return string   移除GET变量后的URL地址
     */
    static public function urlRemoveParam($var, $url = null) {
        $pathinfo_dli = C("PATHINFO_Dli");
        if (!is_null($url)) {
            $url_format = strstr($url, "&") ? $url . '&' : $url . $pathinfo_dli;
            $url = str_replace($pathinfo_dli, "###", $url_format);
            $search = array(
                "/$var" . "###" . ".*?" . "###" . "/",
                "/$var=.*?&/i",
                "/\?&/",
                "/&&/"
            );
            $replace = array(
                "",
                "",
                "?",
                ""
            );
            $url_replace = preg_replace($search, $replace, $url);
            $url_rtrim = rtrim(rtrim($url_replace, "&"), "###");
            return str_replace("###", $pathinfo_dli, $url_rtrim);
        }
        $get = $_GET;
        unset($get[C("VAR_APP")]);
        unset($get[C("VAR_CONTROL")]);
        unset($get[C("VAR_METHOD")]);
        $url = '';
        $url_type = C("URL_TYPE");
        foreach ($get as $k => $v) {
            if ($k === $var)
                continue;
            if ($url_type == 1) {
                $url.=$pathinfo_dli . $k . $pathinfo_dli . $v;
            } else {
                $url.="&" . $k . "=" . $v;
            }
        }
        $url_rtrim = trim(trim($url, $pathinfo_dli), '&');
        $url_str = empty($url_rtrim) ? "" : $pathinfo_dli . $url_rtrim;
        if ($url_type == 1) {
            return __METH__ . $url_str;
        } else {
            return __METH__ . "&" . trim($url_str, "&");
        }
    }

}

?>